export = {};

//接口 是用来描述对象的形状的
// (最常用的是描述字面量形式的对象,或则说一个类的原型)

//最原始的描述方式
// const fn = (obj: /*any*/{ a:string,b:number })=>{
//   return obj.a. //← 具体描述obj这个对象后,能点的出来
// }


//使用type类型别名进行描述
// type XX = { a: string, b: number };
// const fn = (obj: /*any*/XX)=>{
//   return obj.a. //← 具体描述obj这个对象后,能点的出来
// }


//使用接口进行描述
//type XX = {} //←和接口名相同会冲突
interface XX {
  a: string
  lastName: string
}
const fn = (obj: /*any*/XX)=>{
  return obj.a. //← 具体描述obj这个对象后,能点的出来
}



//接口可以被实现被继承 但type不能
//type可以写联合类型, 接口不能

// 接口比type强的地方↓
interface IVegetables {
  taste: string;
  color: string;
}
//
// // 我定义的值比接口中的多 可以采用类型断言 直接断言成对应的接口, 这样就能...了
// const tomato:IVegetables = ({
//   size: 10,
//   taste: 'sour',
//   color: 'red'
// } /*as IVegetables*/)
// // 或则你可以再声明一个同名接口, 和接口会自动和上面的同名接口进行合并
// interface IVegetables {
//   size: number;
// }
//
// 再或则利用接口的继承功能
// interface Itomato extends IVegetables {
//   size: number;
//   type?: string;
// }
// const tomato:Itomato = ({
//   size: 10,
//   taste: 'sour',
//   color: 'red'
// })
//
// 可选、只读
// const tomato:Itomato = ({
//   size: 10,
//   taste: 'sour',
//   color: 'red',
//   type: 'fruit'
// })
//
//但我们可能还有很多属性,并且可能是任意的, 那么我们再使用之前的方式在interface里一个个定义就不太现实了
//我们可以使用可索引类型
interface IVegetables {
  taste: string;
  color: string;
  [key:string/*←也可以是个number,表示是数组成员*/]:any //这样就表示除了taste和color限制死了,其它任意
}
const tomato:IVegetables = ({
  size: 10,
  taste: 'sour',
  color: 'red',
  type: 'fruit',
  [Symbol(1)]:1
})

//2. 描述函数
interface IFullName {
  (firstName: string, lastName: string): string;
}
const fullName:IFullName = (firstName,lastName)=>{
  return firstName + lastName;
}

// 函数身上还有属性
interface ICount{
  (): number;
  count: number;
}
const fn2:ICount = ()=>{
  return ++fn2.count;
}
fn2.count = 1


//3. 描述类
// 无法描述成员的访问权限, 只能描述公有属性和方法(即原型上的东西)
interface Speakable { //接口中的内容都是抽象,没有具体的实现
  name: string;
  speak(): void; //描述类的原型方法; 这里这个位置的void是表示不关心返回的类型,可以是任意类型 (只有类里是这个情况)
}

interface ChineseSpeakable {
  speakChinese():void
}

class Speak implements Speakable,ChineseSpeakable{
  name!: string;

  speak(): string {
    return '123'
  }

  speakChinese(): void {
  }
}

new Speak().speak()

// 类 抽象类 不能被实例化 (一般是父类,不希望实例化的
// 抽象类 相较于 接口, 不仅可以有抽象 也 可以有实现
abstract class Animal {
  abstract name:string // 可以没有实现

  eat(){ //可以有实现
    console.log('eat')
  }

  abstract eat2():void
}
class Tom extends Animal {
  name!: string;

  eat2(): void {
  }
}
new Tom().eat()


//4. 描述类的实例
class Person {
  // public name!:string //如果给构造函数里的参数加上public修饰符, 就不用再在这里声明一下了

  constructor(public name:string) {
    this.name = name
  }
}
// interface IClass {  //表示是一个构造函数类型 （能new）
//   new (name:string): /*类本身也能当做类型,类实例的类型→*/Person
// }
// function createInstance(
//   // clazz:any,
//   // clazz:{ new (name:string):any },
//   //↕等价的
//   // clazz: new (name: string) => any,
//   clazz:IClass,
//
//   name:string
// ){
//   // return new clazz() //TS2554: Expected 1 arguments, but got 0.
//
//   return new clazz(name)
// }
// let r = createInstance(Person,'ahhh')
// r.name
//
//但这有个问题, 我们返回的永远都是Person的实例类型了
//So 泛型的作用就出来了
//
interface IClass<T> {  //表示是一个构造函数类型 （能new）
  new (name:string): /*类本身也能当做类型,类实例的类型→*/T
}
function createInstance<T>(
  // clazz:any,
  // clazz:{ new (name:string):any },
  //↕等价的
  // clazz: new (name: string) => any,
  clazz:IClass<T>,

  name:string
){
  // return new clazz() //TS2554: Expected 1 arguments, but got 0.

  return new clazz(name)
}
let r = createInstance<Person>(Person,'ahhh')
r.name


/** 访问接口里的某个属性*/
interface IPerson {
  name: string;
  age: number;
  address:{
    n: string;
  }
}
// type n = IPerson.address //访问接口的某个属性不能使用 . , 必须使用[]
type n = IPerson['address']['n']
